 /**
 *******************************************************************************
 * @FileName  : user_main.c
 * @Author    : GaoQiu
 * @CreateDate: 2020-05-18
 * @Copyright : Copyright(C) GaoQiu
 *              All Rights Reserved.
 *******************************************************************************
 *
 * The information contained herein is confidential and proprietary property of
 * GaoQiu and is available under the terms of Commercial License Agreement
 * between GaoQiu and the licensee in separate contract or the terms described
 * here-in.
 *
 * This heading MUST NOT be removed from this file.
 *
 * Licensees are granted free, non-transferable use of the information in this
 * file under Mutual Non-Disclosure Agreement. NO WARRENTY of ANY KIND is provided.
 *
 *******************************************************************************
 */
#include "user_main.h"
#include "utils/mdc.h"
#include "utils/pool_buf.h"
#include "utils/msg.h"
#include "utils/stimer.h"
#include "utils/math.h"
#include "utils/heap.h"
#include "utils/byte_stream.h"
#include "utils/lg_mem.h"

#include "stack/controller/ll_api.h"
#include "stack/controller/bb_api.h"
#include "stack/hci_defs.h"

#include "stack/host/dm_api.h"
#include "stack/host/l2cap_api.h"
#include "stack/host/att_api.h"
#include "stack/host/att_uuid.h"
#include "stack/host/smp_api.h"
#include "stack/host/host_cfg.h"

#include "profile/svc_handle.h"

#include "bsp/bsp.h"
#include "sdp.h"
#include "component/kv/kv_api.h"

/*!< Heap Size */
#define APP_HEAP_MEM_SIZE       (8 * 1024)
static uint32_t heap[APP_HEAP_MEM_SIZE/sizeof(uint32_t)] = {0};

/*!< LED blink period Unit:ms */
#define APP_LED_EN              1
#define APP_LED_BLINK_PERIOD    1000
#define APP_KEY_EN              1

/*!< Enable MAC Address filter */
#define APP_MAC_FILTER_EN       0
#define APP_FILTER_MAC          0x66, 0x66, 0x66, 0x66, 0x66, 0x66
#define APP_FILTER_LEN          4

/*!< Enable White-list  */
#define APP_WL_EN               0

/*!< SDP enable/disable */
#define APP_SDP_EN              1

/*! Connection parameter configuration */
#define APP_MAX_MST_NUM         4
#define APP_MAX_SLV_NUM         4
#define APP_MAX_CONN_NUM        (APP_MAX_MST_NUM + APP_MAX_SLV_NUM)

//Adv configuration
#define APP_ADV_EVT_TYPE        DM_ADV_CONN_UNDIRECT
#define APP_ADV_INTR_MIN        ADV_INTR_30_MS
#define APP_ADV_INTR_MAX        ADV_INTR_35_MS
#define APP_ADV_CHN             LL_ADV_CHAN_ALL
#define APP_ADV_FILT            LL_ADV_FILTER_NONE
#define APP_ADV_LOCAL_ADDR_TYPE LL_ADDR_PUBLIC
#define APP_ADV_PEER_ADDR_TYPE  LL_ADDR_PUBLIC
#define APP_ADV_PEER_ADDR       NULL
//Scan configuration
#define APP_SCAN_TYPE           LL_ACTIVE_SCAN
#define APP_SCAN_INTR           SCAN_INTR_50_MS
#define APP_SCAN_WIN            SCAN_INTR_50_MS
#define APP_SCAN_ADDR_TYPE      LL_ADDR_PUBLIC
#define APP_SCAN_FILTER         (APP_WL_EN ? LL_SCAN_FILTER_WL_BIT : LL_SCAN_FILTER_NONE)
//Connection configuration
#define APP_MST_CONN_INTR       CONN_INTR_50_MS
#define APP_MST_TIMEOUT         400
#define APP_MST_CE_LEN          10  //unit:0.625ms
#define APP_SLV_CONN_INTR       CONN_INTR_30_MS
#define APP_SLV_LATENCY         0
#define APP_SLV_TIMEOUT         400

/*!< Sleep Clock Source Selected */
#define APP_SLP_CLK_SRC         CFG_SLEEP_CLOCK_SOURCE
#define APP_SCA_PPM             (APP_SLP_CLK_SRC ? 50:800)

/* ACL Size Setting. */
#ifdef IP_108
#define APP_ACL_SIZE            72
#define APP_LL_ENC_TIME_US      100
#elif defined(IP_107)
#define APP_ACL_SIZE            27
#define APP_LL_ENC_TIME_US      100
#endif

/*! Link layer config. */
const LlCfg_t llCfg = {
	.versNr      = LL_VER_BT_CORE_SPEC, /*!< BLE Core version.           */
	.compId      = LL_COMPANY_ID,       /*!< Company ID.                 */
	.subVersNr   = LL_SUB_VERSION_NUM,  /*!< Implementation version.     */

	.wlNum       = 1,                   /*!< White-list size.            */
	.rlNum       = 0,                   /*!< Resolve list size.          */
	.connCreateTo= 10,                  /*!< Create connection timeout. 0: disable timeout timer, other: timeout time, unit:s */

	/* Extended adv / scan */
	.numAdvSet         = 0,
	.extAdvDataLen     = 0,
	.extScanRspDataLen = 0,
	.advMargin         = 0,             /*!< unit:ms */

	.maxMst      = APP_MAX_MST_NUM,     /*!< Max number of  master connection. */
	.maxSlv      = APP_MAX_SLV_NUM,     /*!< Max number of  slave connection. */
	.maxAclLen   = APP_ACL_SIZE,        /*!< Length of ACL data.         */
	.numRxBufs   = 16,                  /*!< Number of Rx buffer.        */
	.numTxBufs   = 16,                  /*!< Number of Tx buffer.        */
	.localSCA    = APP_SCA_PPM,         /*!< Local sleep clock accuracy. */

	.mstMargin   = APP_MST_CE_LEN,      /*!< Master connection margin. Unit: 0.625ms*/
	.numMoreDat  = 6,                   /*!< the max number of more data. Range: >=1. */

	.sleepClockSrc = APP_SLP_CLK_SRC,   /*!< [Panchip] select sleep clock source. 0:32k RC, 1:32k XTL. */
	.bbSetupTime = 200,                 /*!< [Panchip] BB PHY setup time. Unit:us */
	.llEncTime   = APP_LL_ENC_TIME_US,  /*!< [Panchip] BB CCM time. Unit:us */

	.authPyldTimeoutEn= false,
	.numMaxAnt = 0,
};


/******************************************************************************/
/* Connection Manager                                                         */
/******************************************************************************/

uint8_t A_dataSendStart = 0;
uint32_t A_dataSendTimer = 0;

typedef struct{
	uint32_t        connStartTick;
	uint16_t        connHandle;
	uint16_t        RFU;
	uint8_t         connId;
	uint8_t         role;
	uint8_t         inUse;
	uint8_t         isSmpCmpl;
	uint64_t        peerAddr;
	uint8_t         peerAddrType;

	MdcTimer_t      phyUpdTimer;
	MdcTimer_t      dleUpdTimer;
	MdcTimer_t      connUpdTimer;

	uint16_t        gwHandle;
}AppCCB_t;

/*! Application main control block. */
typedef struct{
	AppCCB_t        ccb[APP_MAX_CONN_NUM];

	MdcHandlerId_t  handlerId;
	MdcTimer_t      timer;
	MdcTimer_t      keyScanTimer;

	uint8_t         numMstConn;
	uint8_t         numSlvConn;

	uint8_t         startConn;
}AppCb_t;

AppCb_t appCb;

#if APP_SDP_EN
typedef struct{
	SdpCb_t         sdpCb; /* SDP */
	uint16_t        connHandle;
	uint8_t         inUse;
}AppSdpCCB_t;

typedef struct{
	AppSdpCCB_t       sdpCcb[APP_MAX_MST_NUM];
}AppSdpCb_t;

AppSdpCb_t  AppSdpCb;
AppSdpCCB_t *APP_NewSdpCb(uint16_t connHandle)
{
	AppSdpCCB_t *pAppSdpCcb = &AppSdpCb.sdpCcb[0];

	for(int i=0; i<COUNTOF(AppSdpCb.sdpCcb); i++, pAppSdpCcb++)
	{
		if(pAppSdpCcb->inUse == false){
			pAppSdpCcb->inUse = true;
			pAppSdpCcb->connHandle = connHandle;

			SDP_Init(&pAppSdpCcb->sdpCb, connHandle);
			return pAppSdpCcb;
		}
	}
	return NULL;
}

void APP_ReleaseSdpCb(uint16_t connHandle)
{
	AppSdpCCB_t *pAppSdpCcb = &AppSdpCb.sdpCcb[0];

	for(int i=0; i<COUNTOF(AppSdpCb.sdpCcb); i++, pAppSdpCcb++)
	{
		if(pAppSdpCcb->inUse && connHandle == pAppSdpCcb->connHandle){
			pAppSdpCcb->inUse = false;
			pAppSdpCcb->connHandle = 0xFFFF;
			SDP_ParamReset(&pAppSdpCcb->sdpCb);
			return;
		}
	}
}

SdpCb_t *APP_GetSdpCb(uint16_t connHandle)
{
	AppSdpCCB_t *pAppSdpCcb = &AppSdpCb.sdpCcb[0];

	for(int i=0; i<COUNTOF(AppSdpCb.sdpCcb); i++,pAppSdpCcb++)
	{
		if(pAppSdpCcb->inUse && connHandle == pAppSdpCcb->connHandle){
			return &pAppSdpCcb->sdpCb;
		}
	}
	return NULL;
}
#endif

AppCCB_t *APP_AllocCcb(uint16_t connHandle, uint8_t role)
{
	AppCCB_t *pCcb = appCb.ccb;

	for(int i=0; i<COUNTOF(appCb.ccb); i++, pCcb++)
	{
		if(pCcb->inUse == false){
			pCcb->inUse = true;
			pCcb->connHandle = connHandle;
			pCcb->role = role;
			pCcb->isSmpCmpl = false;
			pCcb->peerAddr = 0;
			pCcb->connId = i+1;

			/* Start PHY Update Timer*/
			MDC_TimerConfigEx(&pCcb->phyUpdTimer, appCb.handlerId, APP_EVT_PHY_UPD, 0, connHandle);
			MDC_TimerStartMs(&pCcb->phyUpdTimer, 500);

			/* Start DLE Update Timer*/
			MDC_TimerConfigEx(&pCcb->dleUpdTimer, appCb.handlerId, APP_EVT_DLE_UPD, 0, connHandle);
			MDC_TimerStartMs(&pCcb->dleUpdTimer, 1000);

			/* Start Connection Update Timer*/
			MDC_TimerConfigEx(&pCcb->connUpdTimer, appCb.handlerId, APP_EVT_CONN_UPD, 0, pCcb->connId);
			MDC_TimerStartMs(&pCcb->connUpdTimer, 4000);

		#if APP_SDP_EN
			if(pCcb->role == LL_ROLE_MASTER){
				APP_NewSdpCb(connHandle);
			}
		#endif
			return pCcb;
		}
	}
	return NULL;
}

void APP_ReleaseCcbByHandle(uint16_t connHandle)
{
	AppCCB_t *pCcb = appCb.ccb;

	for(int i=0; i<COUNTOF(appCb.ccb); i++, pCcb++)
	{
		if(pCcb->connHandle == connHandle && pCcb->inUse){
			pCcb->inUse = false;
			pCcb->connId = 0;

			MDC_TimerStop(&pCcb->phyUpdTimer);
			MDC_TimerStop(&pCcb->dleUpdTimer);
			MDC_TimerStop(&pCcb->connUpdTimer);

		#if APP_SDP_EN
			APP_ReleaseSdpCb(connHandle);
		#endif
			return;
		}
	}
}

AppCCB_t *APP_GetConnCb(uint16_t connHandle)
{
	AppCCB_t *pCcb = appCb.ccb;

	for(int i=0; i<COUNTOF(appCb.ccb); i++, pCcb++)
	{
		if(pCcb->connHandle == connHandle && pCcb->inUse){
			return pCcb;
		}
	}
	return NULL;
}

void APP_DisconnectAll(void)
{
	AppCCB_t *pCcb = appCb.ccb;
	for(int i=0; i<COUNTOF(appCb.ccb); i++, pCcb++)
	{
		if(pCcb->inUse){
			HCI_Disconnect(pCcb->connHandle, HCI_ERR_REMOTE_USER_TERM_CONN);
		}
	}
}

void APP_MsgHandler(MdcEventMask_t event, MdcMsgHdr_t *pMsg)
{
	static uint8_t keyScanCnt = 0;

	if(pMsg)
	{
		switch(pMsg->event)
		{
		case APP_EVT_LED_BLINK:
			BSP_LedToggle(LED_PIN_GREEN);
			MDC_TimerStartMs(&appCb.timer, APP_LED_BLINK_PERIOD);
			break;

		case APP_EVT_PHY_UPD:
		{
			uint16_t connHandle = pMsg->param;
			UNUSED(connHandle);

			//HCI_LE_SetPhy(connHandle, 0x00, LL_PHYS_LE_1M_BIT, LL_PHYS_LE_1M_BIT, LL_PHY_OPTIONS_NONE);
			//HCI_LE_SetPhy(connHandle, 0x00, LL_PHYS_LE_2M_BIT, LL_PHYS_LE_2M_BIT, LL_PHY_OPTIONS_NONE);
			//HCI_LE_SetPhy(connHandle, 0x00, LL_PHYS_LE_CODED_BIT, LL_PHYS_LE_CODED_BIT, LL_PHY_OPTIONS_S8_PREFERRED);
			//HCI_LE_SetPhy(connHandle, 0x00, LL_PHYS_LE_CODED_BIT, LL_PHYS_LE_CODED_BIT, LL_PHY_OPTIONS_S2_PREFERRED);
			//HCI_LE_SetPhy(connHandle, 0x00, LL_PHYS_LE_1M_BIT|LL_PHYS_LE_2M_BIT, LL_PHYS_LE_1M_BIT|LL_PHYS_LE_2M_BIT, LL_PHY_OPTIONS_NONE);

			//APP_TRACK_WRN("Start PHY Upd...\r\n");
			break;
		}
		case APP_EVT_DLE_UPD:
		{
			uint16_t connHandle = pMsg->param;
			UNUSED(connHandle);

			//HCI_LE_SetDataLen(connHandle, llCfg.maxAclLen, LL_MAX_TX_RX_TIME_1M(llCfg.maxAclLen));
			//APP_TRACK_WRN("Start DLE Upd...\r\n");
			break;
		}
		case APP_EVT_CONN_UPD:
		{
			if(pMsg->param == 0 || pMsg->param > APP_MAX_CONN_NUM)
				break;

			AppCCB_t *pCCB = &appCb.ccb[pMsg->param - 1]; //pMsg->param -> connId
			if(pCCB->role == DM_ROLE_SLAVE){
				L2C_SlvConnUpdateReq(pCCB->connHandle, APP_SLV_CONN_INTR, APP_SLV_CONN_INTR,
						                               APP_SLV_LATENCY,   APP_SLV_TIMEOUT);
				APP_TRACK_WRN("Slave L2CAP Send conn update req - handle:%d\r\n", pCCB->connHandle);
			}
			else{
				//extern void APP_MstConnUpd(uint16_t connHandle, uint16_t intvl, uint16_t latency, uint16_t timeout);
				//APP_MstConnUpd(pCCB->connHandle, CONN_INTR_50_MS, 0, 400);
				//APP_TRACK_WRN("Master Send conn update - handle:%d\r\n", pCCB->connHandle);
			}
			break;
		}
	#if APP_KEY_EN
		case APP_EVT_KEY_SCAN:
		{
			App_KeyTaskStart();

			if(keyScanCnt++ < 5){
				App_KeyScanTimerStart();
			}
			else{
				keyScanCnt = 0;
			}
			break;
		}
	#endif

		default:
			break;
		}
	}

	if(event)
	{
	#if APP_KEY_EN
		if(event & APP_EVT_KEY_SCAN){
			//App_KeyTaskStart();
			App_KeyScanTimerStart();
			keyScanCnt = 0;
		}
	#endif
	}
}

/******************************************************************************/
/* Key                                                                        */
/******************************************************************************/
void App_SendKeyReport(uint16_t attHandle, uint8_t *pData, uint32_t len)
{
	AppCCB_t *pCcb = &appCb.ccb[0];
	for(int i=0; i<APP_MAX_CONN_NUM; i++, pCcb++)
	{
		if(pCcb->inUse && pCcb->role == DM_ROLE_SLAVE){
			ATTS_ValueNotify(pCcb->connHandle, attHandle, pData, len);
		}
	}
}

#if APP_KEY_EN
static uint8_t  keyPressed  = false;
static uint8_t  keyValid = false;

void App_KeyScanTimerStart(void)
{
	MDC_TimerStartMs(&appCb.keyScanTimer, 4);
}

void App_BtnEvtCb(void)
{
	MDC_SendEvent(appCb.handlerId, APP_EVT_KEY_SCAN);
}

void App_KeyInit(void)
{
	Btn_Init();

#if CFG_BTN_IT_MODE_EN
	Btn_RegisterEvtHandler(App_BtnEvtCb);
#endif
}

void App_KeyChangeHandler(void)
{
	BtnData_t *pBtnData = Btn_GetData();

	uint16_t consumerKey = 0;
	keyPressed = true;

	switch(pBtnData->cnt)
	{
	case 0:
		APP_TRACK_INFO("Key release:0x%02x\r\n", pBtnData->code[0]);

		if(keyValid){
			keyValid = false;
			App_SendKeyReport(HID_INPUT_REPORT_2_HDL, (uint8_t*)&consumerKey, 2);
		}
		appCb.startConn = false;

		break;

	case 1:
		APP_TRACK_INFO("Key press:0x%02x\r\n", pBtnData->code[0]);

		if(pBtnData->code[0] == BTN_1){ //UNPAIR
			consumerKey = MKEY_VOL_DN;

			/* Disconnect all connections. */
			APP_TRACK_INFO("Disconnect All...\r\n");
			APP_DisconnectAll();
		}
		else if(pBtnData->code[0] == BTN_2){ //PAIR
			consumerKey = MKEY_VOL_UP;

			/* Trigger connection. */
			APP_TRACK_INFO("Start Connection...\r\n");
			appCb.startConn = true;
		}
		else if(pBtnData->code[0] == BTN_3)
		{
			A_dataSendStart = !A_dataSendStart;
			APP_TRACK_INFO("Send Data: %s\r\n", A_dataSendStart ? "Enable":"Disable");
		}
		if(consumerKey){
			keyValid = true;
			App_SendKeyReport(HID_INPUT_REPORT_2_HDL, (uint8_t*)&consumerKey, 2);
		}
		break;

	case 2:
		break;

	default:
		break;
	}
}

void App_KeyTaskStart(void)
{
#if !CFG_BTN_IT_MODE_EN
	static uint32_t keyScanTick = 0;
	if(!IsTimeExpired(keyScanTick, 20 * 1000)){  //keyScan interval: 20mS
		return;
	}
	keyScanTick = GetCurSysTick();
#endif

	bool_t keyChanged = Btn_Scan();
	if(keyChanged)
	{
		App_KeyChangeHandler();
	}
}
#endif

/******************************************************************************/
/* Event Call-back                                                            */
/******************************************************************************/
#if APP_WL_EN
uint8_t addr0[] = {0x66, 0x66, 0x66, 0x66, 0x00, 0x00};
uint8_t addr1[] = {0x66, 0x66, 0x66, 0x66, 0x11, 0x11};
uint8_t addr2[] = {0x66, 0x66, 0x66, 0x66, 0x22, 0x22};
uint8_t addr3[] = {0x66, 0x66, 0x66, 0x66, 0x33, 0x33};
uint8_t addr4[] = {0x66, 0x66, 0x66, 0x66, 0x88, 0x88};
uint8_t addr5[] = {0x66, 0x66, 0x66, 0x66, 0xDD, 0xDD};
uint8_t addr6[] = {0x66, 0x66, 0x66, 0x66, 0xEE, 0xEE};
#endif

uint8_t  A_connCreateFlag = 0;
uint32_t A_createConnStart = 0;
uint32_t A_disconnStart = 0;
uint32_t A_connCnt = 0;
uint8_t remoteAddr[6] = {0x66, 0x66, 0x66, 0x66, 0x00, 0xB2};

LlInitParam_t initParam = {
	APP_SCAN_INTR,       /*!< scan interval */
	APP_SCAN_WIN,        /*!< scan window size */
	LL_INIT_FILTER_PEER, /*!< filter policy */
	LL_ADDR_PUBLIC,      /*!< local address type */
	LL_ADDR_PUBLIC,      /*!< peer address type */
	&remoteAddr[0],      /*!< local address */
};

LlConnSpec_t connSpec = {
	APP_MST_CONN_INTR,  /*!< Min connection interval */
	APP_MST_CONN_INTR,  /*!< Max connection interval */
	0,                  /*!< latency */
	APP_MST_TIMEOUT,    /*!< timeout */
	APP_MST_CE_LEN,     /*!< min connection event(CE) length, unit:0.625ms */
	APP_MST_CE_LEN,     /*!< max connection event(CE) length, unit:0.625ms */
};

void APP_MstConnUpd(uint16_t connHandle, uint16_t intvl, uint16_t latency, uint16_t timeout)
{
	LlConnSpec_t connSpec;
	connSpec.connIntervalMin = intvl;
	connSpec.connIntervalMax = intvl;
	connSpec.connLatency     = latency;
	connSpec.supTimeout      = timeout;
	connSpec.minCeLen        = APP_MST_CE_LEN;
	connSpec.maxCeLen        = APP_MST_CE_LEN;

	HCI_LE_ConnUpd(connHandle, &connSpec);
}

void DM_EvtHandler(DmEvt_t *pEvt)
{
	switch(pEvt->hdr.event)
	{
	case DM_ADV_REPORT_IND:
	{
		HciLeAdvReportEvt_t *p = (HciLeAdvReportEvt_t *)pEvt;

	#if APP_MAC_FILTER_EN
		// user address filter
		uint8_t filterMac[] = {APP_FILTER_MAC};
		if(memcmp((uint8_t*)&p->addr, filterMac, APP_FILTER_LEN)){
			return;
		}
	#endif

	#if 0
		char str[40] = {0};
		BdaToString(str, sizeof(str)-1, &p->addr[0], p->addrType);
		APP_TRACK_INFO("Device Found: %s (RSSI %d)\r\n", str, p->rssi);
	#endif

		if(p->eventType >= HCI_ADV_DISC_UNDIRECT || p->rssi < -60){
			return;
		}

		if(appCb.numMstConn >= APP_MAX_MST_NUM || A_connCreateFlag){
			return;
		}

		if(appCb.startConn)
		{
			APP_TRACK_INFO("Start Connection Create...\r\n\t-peer_addr:");
			APP_TRACK_DATA((uint8_t *)&p->addr, 6);
			appCb.startConn = false;
			memcpy(remoteAddr, p->addr, 6);
			initParam.peerAddrType = p->addrType;
			uint8_t res = HCI_LE_CreateConn(&initParam, &connSpec);
			if(res == BLE_OK){
				A_connCreateFlag = true;
			}
		}
		break;
	}
	case DM_CONN_OPEN_IND:
	{
		A_connCreateFlag = false;

		if(pEvt->leConnCmpl.status){//Create connection cancel
			if(appCb.numMstConn < APP_MAX_MST_NUM){
				DM_LE_SetScanEnable(ENABLE, DISABLE);
			}
			APP_TRACK_WRN("Create connection timeout, Restart scan...\r\n");
			return;
		}

		A_connCnt++;

		APP_TRACK_INFO("******Connection Cmpl******\r\n"
				"\t-role: %s\r\n\t-connHandle: %d\r\n\t-peer addr: ",
				pEvt->leConnCmpl.role == LL_ROLE_MASTER ? "Master":"Slave", pEvt->leConnCmpl.handle);
		TRACK_DATA((uint8_t*)&pEvt->leConnCmpl.peerAddr, 6);

		BSP_LedOn(LED_PIN_RED);
		A_createConnStart = GetCurSysTick()|1;
		A_dataSendStart = false;

		AppCCB_t *pCCB = APP_AllocCcb(pEvt->leConnCmpl.handle, pEvt->leConnCmpl.role);
		if(pCCB){
			pCCB->connStartTick = GetCurSysTick()|1;
			pCCB->peerAddr     = pEvt->leConnCmpl.peerAddr;
			pCCB->peerAddrType = pEvt->leConnCmpl.peerAddrType;
		}

		if(pEvt->leConnCmpl.role == DM_ROLE_MASTER){
			appCb.numMstConn++;
			if(appCb.numMstConn < APP_MAX_MST_NUM){
				DM_LE_SetScanEnable(ENABLE, DISABLE);
			}
			HCI_ReadRemoteVerInfo(pEvt->leConnCmpl.handle);
		}
		else if(pEvt->leConnCmpl.role == DM_ROLE_SLAVE){
			appCb.numSlvConn++;
			if(appCb.numSlvConn < APP_MAX_SLV_NUM){
				DM_LE_SetAdvEnable(ENABLE);
			}
		}
		break;
	}
	case DM_CONN_CLOSE_IND:
	{
		A_connCnt--;
		AppCCB_t *pCCB = APP_GetConnCb(pEvt->disconnectCmpl.handle);
		uint8_t role = pCCB->role;

		APP_TRACK_INFO("******Disconnection Cmpl******\r\n"
				"\t-connHandle: 0x%02x\r\n"
				"\t-reason: 0x%02x\r\n"
				"\t-peerAddrType:%u\r\n"
				"\t-peerAddr:",
				pEvt->disconnectCmpl.handle,
				pEvt->disconnectCmpl.reason,
				pCCB->peerAddrType);
		APP_TRACK_DATA((uint8_t *)&pCCB->peerAddr, 6);

		BSP_LedOff(LED_PIN_RED);
		A_disconnStart = GetCurSysTick()|1;

		if(appCb.numMstConn && role == DM_ROLE_MASTER){
			appCb.numMstConn--;
			DM_LE_SetScanEnable(ENABLE, DISABLE);
			APP_TRACK_INFO("app scan enable...\r\n");
		}
		else if(appCb.numSlvConn && role == DM_ROLE_SLAVE){
			appCb.numSlvConn--;
			DM_LE_SetAdvEnable(ENABLE);
			APP_TRACK_INFO("app adv enable...\r\n");
		}
		/* Release Connection */
		APP_ReleaseCcbByHandle(pEvt->disconnectCmpl.handle);
		break;
	}
	case DM_CONN_READ_REMOTE_VER_IND:
		APP_TRACK_INFO("Remote Version: %d\r\n\t-manufacturer ID:0x%04x\r\n",
				pEvt->readRemoteVerInfoCmpl.version,pEvt->readRemoteVerInfoCmpl.mfrName);
		HCI_LE_ReadRemoteFeat(pEvt->readRemoteVerInfoCmpl.handle);
		break;

	case DM_CONN_LE_READ_REMOTE_FEAT_IND:
		APP_TRACK_INFO("LE Remote Feature: 0x%02x\r\n",pEvt->leReadRemoteFeatCmpl.features[0]);
		break;

	case DM_CONN_DATA_LEN_CHANGE_IND:
		APP_TRACK_INFO("LE data length changed\r\n\t-max tx: %d B\r\n\t-max rx: %d B\r\n",
				       pEvt->leDataLenChange.maxTxOctets, pEvt->leDataLenChange.maxRxOctets);
		break;

	case DM_CONN_PHY_UPD_IND:
		APP_TRACK_INFO("Phy upd cmpl\r\n\t-Phy:%d\r\n", pEvt->lePhyUpdCmpl.txPhy);
		break;

	case DM_CONN_CH_SEL_IND:
		APP_TRACK_INFO("Chn Select cmpl\r\n\t-Chn:%d\r\n", pEvt->leChnSelInd.chSelAlg);
		break;

	case DM_L2C_CONN_PARAM_UPD_IND:
		break;

	case DM_CONN_UPD_IND:
	{
		APP_TRACK_INFO("Conn Upd Cmpl:\r\n"
				"\t-handle:%d\r\n"
				"\t-intvl:%d us\r\n"
				"\t-latency:%d\r\n"
				"\t-to:%d ms\r\n",
				pEvt->leConnUpdateCmpl.handle,
			    pEvt->leConnUpdateCmpl.connInterval * UNIT_1_25_MS,
			    pEvt->leConnUpdateCmpl.connLatency,
			    pEvt->leConnUpdateCmpl.supTimeout * 10);
		break;
	}
	case DM_SMP_PIN_CODE_REQ:
	{
		uint32_t pinCode = 123456;
		SMP_SetPinCode(pEvt->smpPinCodeReq.connHandle, pinCode);
		APP_TRACK_INFO("%6d\r\n", pinCode);
		break;
	}
	case DM_SMP_PAIR_END_IND:
	{
		if(pEvt->smpPairEndInd.status){
			APP_TRACK_INFO("Pair failed...connHandle: 0x%02x\r\n", pEvt->smpPairEndInd.connHandle);
		}
		else{
			APP_TRACK_INFO("Pair successfully...connHandle: 0x%02x\r\n", pEvt->smpPairEndInd.connHandle);
			AppCCB_t *pCcb = APP_GetConnCb(pEvt->smpPairEndInd.connHandle);
			if(pCcb){
				pCcb->isSmpCmpl = true;
			}
		}
		#if APP_SDP_EN
			SmpPairEndInd_t *pCmpl = (SmpPairEndInd_t *)pEvt;
			AppCCB_t *pCcb = APP_GetConnCb(pCmpl->connHandle);
			if(pCcb && pCcb->role == LL_ROLE_MASTER){
				SdpCb_t *pSdpCb = APP_GetSdpCb(pCcb->connHandle);
				SDP_Start(pSdpCb);
			}
		#endif
		break;
	}

	default:
		break;
	}
}

void ATT_EvtHandler(AttEvt_t *pEvt)
{
	AppCCB_t *pCcb = APP_GetConnCb(pEvt->handle);
	if(pCcb == NULL){
		return;
	}
#if APP_SDP_EN
	SdpCb_t *pSdpCb = APP_GetSdpCb(pEvt->handle);
	if(pSdpCb){
		SDP_AttDataHandler(pSdpCb, pEvt->hdr.event, pEvt->pValue, pEvt->valueLen);
	}
#endif

	switch(pEvt->hdr.event)
	{
	case ATTC_HANDLE_VALUE_NTF:
	{
	#if APP_SDP_EN
		if(pSdpCb == NULL)
			return;

		uint16_t attHandle = 0;
		uint16_t keyCode = 0;
		uint16_t uuid = ATT_UUID_HID_REPORT;
		uint8_t *pPkt = pEvt->pValue;

		pPkt++; //skip opcode
		BSTREAM_TO_UINT16(attHandle, pPkt);
		BSTREAM_TO_UINT16(keyCode, pPkt);

		APP_TRACK_INFO("RX Notify(AttHandle:%d,len:%d,rxCnt:%u): \r\n", attHandle, pEvt->valueLen-3, A_rx_pkt_cnt);
		APP_TRACK_DATA(pEvt->pValue + 3, MIN(pEvt->valueLen-3, 23));

		Att_t *pAtt = SDP_FindCharByHandle(pSdpCb, attHandle);
		if(pAtt && pAtt->valueUuid.len == ATT_16_UUID_LEN && pAtt->valueUuid.uuid_16 == uuid)
		{
			if(keyCode == 0x00E9 || keyCode == VOL_UP){
				BSP_LedToggle(LED_PIN_BLUE);
			}
			else if(keyCode == 0x00EA || keyCode == VOL_DN){
				BSP_LedToggle(LED_PIN_BLUE);
			}
		}
	#endif
		break;
	}

	case ATTC_HANDLE_VALUE_IND:
		//APP_TRACK_WRN("Send value confirm\r\n");
		ATTC_SendValueConfirm(pCcb->connHandle);
		break;

	case ATT_MTU_UPDATE_IND:
		APP_TRACK_INFO("MTU Update Cmpl\r\n\t-MTU size:%d B\r\n", pEvt->mtu);
		break;

	default:
		break;
	}
}

#if APP_SDP_EN
void SDP_EvtHandler(SdpEvt_t *pEvt)
{
	switch(pEvt->evtId)
	{
	case SDP_EVT_SDP_END:
	{
		AppCCB_t *pCCB = APP_GetConnCb(pEvt->connHandle);
		if(pCCB == NULL)
			return;

		SdpCb_t *pSdpCb = APP_GetSdpCb(pEvt->connHandle);
		if(pSdpCb == NULL)
			return;

		uint8_t UUID[] = {0x79, 0x41, 0xDC, 0x24, 0x0E, 0xE5, 0xA9, 0xE0,
			              0x93, 0xF3, 0xA3, 0xB5, 0x02, 0x00, 0x40, 0x6E};
		Att_t *pChr = SDP_FindCharByUUID(pSdpCb, UUID, 16);
		if(pChr){
			pCCB->gwHandle = pChr->valueHandle;
			APP_TRACK_INFO(" ===> GW RX ATT Handle: 0x%04x(%d)\r\n", pCCB->gwHandle, pCCB->gwHandle);
		}
		break;
	}
	default:
		break;
	}
}
#endif

void APP_Init(void)
{
	appCb.handlerId = MDC_AddMsgHandler(APP_MsgHandler);
#if APP_LED_EN
	MDC_TimerConfigEx(&appCb.timer, appCb.handlerId, APP_EVT_LED_BLINK, 0, 0);
	MDC_TimerStartMs(&appCb.timer, APP_LED_BLINK_PERIOD);
#endif

#if APP_KEY_EN
	/* keyboard initialization. */
	App_KeyInit();

	/* Key Scan Timer initialization. */
	MDC_TimerConfigEx(&appCb.keyScanTimer, appCb.handlerId, APP_EVT_KEY_SCAN, 0, 0);
#endif
}

void APP_Start(void)
{
#if APP_SDP_EN
	for(int i=0; i<APP_MAX_MST_NUM; i++){
		SDP_Handler(&AppSdpCb.sdpCcb[i].sdpCb);
	}
#endif

#if APP_KEY_EN && !CFG_BTN_IT_MODE_EN
	App_KeyTaskStart();
#endif
}

#if 0
uint8_t data1[] = {//51, 0x00, 0x04, 0x00, 0x1B, 26, 0x00,
  0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
  0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
  0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
  0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
  0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,//80
  0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
  0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
  0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
  0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
  0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,//80
  0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
  0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
  0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
  0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
  0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,//80
  0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66,
};

uint8_t dataCnt[APP_MAX_CONN_NUM] = {0};
#endif
/**
 * @brief: setup
 */
void Setup(void)
{
	/* memory initialization. */
	lg_mem_init();
	HeapInit((uint8_t *)&heap[0], APP_HEAP_MEM_SIZE);

	/* BSP initialization. */
	BSP_InitAllLed();

	/* kv database initialization. */
	kv_init();

	/* Buffer pool alloc. */
	pool_desc_t pools[] ={
		{16,     8},
		{32,     4},
		{80,     8},
	    {280,    4},
	};
	PoolBufInit(pools, COUNTOF(pools));

	/* MDC system initialization. */
	MDC_Init();
	MDC_TimerInit();/*!< MDC Timer initialization. */

	/* Set MAC address. */
	uint8_t bdAddr[6] = {0x66, 0x66, 0x66, 0x66, 0x00, 0xC3};
	GetConfigParam(CFG_PARAM_MAC_ADDR, bdAddr, 6);
	LL_SetBdAddr(bdAddr, BD_ADDR_LEN);

	APP_TRACK_INFO("BD_ADDR:");
	APP_TRACK_DATA(bdAddr, BD_ADDR_LEN);

	/* LL initialization. */
	LL_Init(&llCfg);
	LL_MstInit();
	LL_SlvInit();
	//LL_ConnEnableRxTime(FALSE);
	BB_SetTxPower(CFG_TX_POWER_LVL);

	/* Host initialization. */
	hostCfg.numMaxConn = APP_MAX_CONN_NUM;

	/* HCI initialization. */
	HCI_Init();
	HCI_SetMaxAclLen(247 + 4);/*!< Set L2CAP payload size= 104B ==> ATT MTU Size = 100. */

	/* DM initialization. */
	DM_Init();
	DM_AdvInit();
	DM_ScanInit();
	DM_SecInit();
	DM_EvtRegister(DM_EvtHandler);
	DM_SecSetInitiateSecEnable(true);
//	DM_SecSetAuth(SMP_AUTH_MITM_FLAG);
//	DM_SecSetIoCap(IO_CAP_KEYBOARD_ONLY);
//	DM_SecSetAuth(SMP_AUTH_BOND_FLAG|SMP_AUTH_MITM_FLAG);
//	DM_SecSetIoCap(IO_CAP_DISPLAY_ONLY);
//	DM_SecSetDefPinCode(123456, true);

	/* L2CAP initialization. */
	L2C_Init();
	L2C_SlvInit();
	L2C_MstInit();

	/* ATT initialization. */
	ATT_Init();
	ATTS_Init();
	ATTC_Init();
	ATT_EvtRegister(ATT_EvtHandler);
	ATTS_CccRegister(&ServerCccSets[0], CCC_IDX_MAX, NULL);

	/* Register GATT table */
	ATTS_AddAttGroup(&gapGrp);
	ATTS_AddAttGroup(&gattGrp);
	ATTS_AddAttGroup(&battGrp);
	ATTS_AddAttGroup(&hidGrp);
	ATTS_AddAttGroup(&gwGrp);
	ATTS_AddAttGroup(&htsGrp);

	/* SMP initialization. */
#if 0
	SMP_NonInit();
#else
	SMP_Init();
	SMP_RespInit();
	SMP_InitiatorInit();
#endif

#if APP_MAX_SLV_NUM
	/* ADV initialization. */
	DmAdvParam_t advParam = {
		.minInterval  = APP_ADV_INTR_MIN,
		.maxInterval  = APP_ADV_INTR_MAX,
		.advType      = APP_ADV_EVT_TYPE,
		.ownAddrType  = APP_ADV_LOCAL_ADDR_TYPE,
		.peerAddrType = APP_ADV_PEER_ADDR_TYPE,
		.pPeerAddr    = NULL,
		.chnMap       = APP_ADV_CHN,
		.filter       = APP_ADV_FILT,
	};
	DM_LE_SetAdvParam(&advParam);

	uint8_t advData[] = {
		/*!< Flag type.  */
		0x02, 0x01, 0x06,

		/*!< short name. */
		0x04, 0x08, 'H','I','D',

		/*!< 384, Generic Remote Control, Generic category */
		0x03, 0x19, 0x80, 0x01,

		/*!< service UUID */
		0x03, 0x02, 0x12, 0x18,
	};

	uint8_t scanRsp[] = {
		/*!< complete name. */
		0x04, 0x09, 'H','O','D',
	};
	DM_LE_SetAdvData(advData, sizeof(advData));
	DM_LE_SetScanRspData(scanRsp, sizeof(scanRsp));

	DM_LE_SetAdvEnable(ENABLE);
#endif

#if APP_MAX_MST_NUM
    /* Scan initialization. */
	DmScanParam_t scanParam = {
		.scanType      = APP_SCAN_TYPE,
		.scanInterval  = APP_SCAN_INTR,
		.scanWindow    = APP_SCAN_WIN,
		.ownAddrType   = APP_SCAN_ADDR_TYPE,
		.scanFilter    = APP_SCAN_FILTER,
	};
	DM_LE_SetScanParam(&scanParam);

	DM_LE_SetScanEnable(ENABLE, DISABLE);
#endif

#if APP_WL_EN
	/* Setting Whitelist */
	DM_LE_AddDeviceToWhiteList(DM_ADDR_PUBLIC, addr0);
	DM_LE_AddDeviceToWhiteList(DM_ADDR_PUBLIC, addr1);
	DM_LE_AddDeviceToWhiteList(DM_ADDR_PUBLIC, addr2);
	DM_LE_AddDeviceToWhiteList(DM_ADDR_PUBLIC, addr3);
	DM_LE_AddDeviceToWhiteList(DM_ADDR_PUBLIC, addr4);
	DM_LE_AddDeviceToWhiteList(DM_ADDR_PUBLIC, addr5);
	DM_LE_AddDeviceToWhiteList(DM_ADDR_PUBLIC, addr6);
#endif

#if APP_SDP_EN
	SDP_RegisterEvtCback(SDP_EvtHandler);
#endif

	/* Application initialization. */
	APP_Init();
}

bool_t ATT_TxBufAvailable(uint16_t connHandle)
{
	uint32_t minNum = ((4 + ATT_GetMtu(connHandle)) / pLlConfig->maxAclLen + 1);
	return LL_GetFreeTxBufNum(connHandle) >= minNum;
}

/**
 * @brief: loop
 */
uint32_t A_loop = 0;
void Loop(void)
{
	A_loop++;

	MDC_Start();

	APP_Start();

	if(A_createConnStart && IsTimeExpired(A_createConnStart, 10000*1000))
	{
		A_dataSendStart = GetCurSysTick()|1;
		A_createConnStart = 0;
	}

#if 0
	if(A_dataSendStart && IsTimeExpired(A_dataSendStart, 1000*1000))
	{
		A_dataSendTimer = GetCurSysTick()|1;

		AppCCB_t *pCcb = appCb.ccb;
		for(int i=0; i<APP_MAX_CONN_NUM; i++, pCcb++)
		{
			if(pCcb->inUse)
			{
				if(pCcb->role == DM_ROLE_MASTER && ATT_TxBufAvailable(pCcb->connHandle)){
					uint8_t *p = data1;
					uint32_t dataLen = sizeof(data1);
					uint32_t res = ATT_SUCCESS;
					UNUSED(res);

					p[0] = dataCnt[pCcb->connHandle];
					uint16_t attHandle = pCcb->gwHandle;
					if((res = ATTC_SendWriteCmd(pCcb->connHandle, attHandle, p, dataLen)) == ATT_SUCCESS){
						dataCnt[pCcb->connHandle]++;
					}
					else{
						//APP_TRACK_ERR("send data failed-res:%02X, connHandle:%02x\r\n", res, pCcb->connHandle);
					}
				}
				else if(pCcb->role == DM_ROLE_SLAVE && ATT_TxBufAvailable(pCcb->connHandle)){
					uint8_t *p = data1;
					p[0] = dataCnt[pCcb->connHandle];
					if(GW_SendData(pCcb->connHandle, p, sizeof(data1)) == BLE_OK){
						dataCnt[pCcb->connHandle]++;
					}
				}
				else{
					//APP_TRACK_WRN("ll tx buf num:%d\r\n", LL_GetFreeTxBufNum(pCcb->connHandle));
				}
			}
		}
	}
#endif
}
